/**
 * 
 */
package kr.co.insoft.driver.amazon;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import kr.co.insoft.driver.Drivers;
import kr.co.insoft.driver.amazon.model.AmazonCredentialModel;
import kr.co.insoft.driver.amazon.model.AmazonHardwareProfileModel;
import kr.co.insoft.driver.amazon.model.AmazonImageModel;
import kr.co.insoft.driver.amazon.model.AmazonInstanceModel;
import kr.co.insoft.driver.amazon.model.AmazonKeyPairModel;
import kr.co.insoft.driver.amazon.model.AmazonOsTypeModel;
import kr.co.insoft.driver.amazon.model.AmazonRegionModel;
import kr.co.insoft.driver.amazon.model.AmazonSecurityModel;
import kr.co.insoft.driver.amazon.model.AmazonVolumeModel;
import kr.co.insoft.driver.amazon.model.AmazonZoneModel;
import kr.co.insoft.driver.exception.DriverException;
import kr.co.insoft.driver.exception.ExceptionCode;
import kr.co.insoft.driver.exception.NotSupportedException;
import kr.co.insoft.driver.model.HardwareProfileModel;
import kr.co.insoft.driver.model.ImageModel;
import kr.co.insoft.driver.model.InstanceModel;
import kr.co.insoft.driver.model.InstanceResultModel;
import kr.co.insoft.driver.model.KeyPairModel;
import kr.co.insoft.driver.model.SecurityModel;
import kr.co.insoft.driver.model.VolumeModel;
import kr.co.insoft.driver.model.ZoneModel;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.AvailabilityZone;
import com.amazonaws.services.ec2.model.CreateTagsRequest;
import com.amazonaws.services.ec2.model.CreateVolumeRequest;
import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesRequest;
import com.amazonaws.services.ec2.model.DescribeImagesRequest;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.DescribeInstancesResult;
import com.amazonaws.services.ec2.model.DescribeKeyPairsRequest;
import com.amazonaws.services.ec2.model.DescribeSecurityGroupsRequest;
import com.amazonaws.services.ec2.model.DescribeVolumesRequest;
import com.amazonaws.services.ec2.model.DescribeVolumesResult;
import com.amazonaws.services.ec2.model.Image;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceStateChange;
import com.amazonaws.services.ec2.model.InstanceType;
import com.amazonaws.services.ec2.model.KeyPairInfo;
import com.amazonaws.services.ec2.model.Region;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.SecurityGroup;
import com.amazonaws.services.ec2.model.StartInstancesRequest;
import com.amazonaws.services.ec2.model.StartInstancesResult;
import com.amazonaws.services.ec2.model.StopInstancesRequest;
import com.amazonaws.services.ec2.model.StopInstancesResult;
import com.amazonaws.services.ec2.model.Tag;
import com.amazonaws.services.ec2.model.TerminateInstancesRequest;
import com.amazonaws.services.ec2.model.TerminateInstancesResult;
import com.amazonaws.services.ec2.model.Volume;

/**
 * 
 * 
 * @author IN-SOFT Inc. GoodwillDD(sylee@in-soft.co.kr)
 * 
 */
public abstract class AmazonDriver implements Drivers, AmazonProcess {

	/**
	 * 
	 */
	private static final long serialVersionUID = 2668786803651560373L;

	protected static final Logger logger = LoggerFactory
			.getLogger(AmazonDriver.class);

	protected AmazonCredentialModel amazonCredential;

	public AmazonDriver() {
		this.amazonCredential = new AmazonCredentialModel();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.INSoftDriver#setAccessKey(java.lang.String)
	 */
	@Override
	public void setAccessKey(String accessKey) {
		this.amazonCredential.setAccessKey(accessKey);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.INSoftDriver#setApiKey(java.lang.String)
	 */
	@Override
	public void setApiKey(String apiKey) {
		this.amazonCredential.setAccessKey(apiKey);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.INSoftDriver#setEndpoint(java.lang.String)
	 */
	@Override
	public void setEndpoint(String endpoint) {
		this.amazonCredential.setEndpoint(endpoint);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.INSoftDriver#setSecretKey(java.lang.String)
	 */
	@Override
	public void setSecretKey(String secretKey) {
		this.amazonCredential.setSecretKey(secretKey);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.INSoftDriver#setUsername(java.lang.String)
	 */
	@Override
	public void setUsername(String username) {
		this.amazonCredential.setUsername(username);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.Drivers#setProvider(java.lang.String)
	 */
	@Override
	public void setProvider(String provider) {
		this.amazonCredential.setProvider(provider);
	}

	/**
	 * 계정 정보 Validation 후, Client 반환
	 * 
	 * @return
	 * @throws DriverException
	 */
	private AmazonEC2 getClient() throws DriverException, NotSupportedException {
		try {
			Assert.notNull(amazonCredential);
			Assert.notNull(amazonCredential.getAccessKey());
			Assert.notNull(amazonCredential.getSecretKey());

			return new AmazonEC2Client(
					this.amazonCredential.getAmazonCredentials());
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.amazon.Drivers#getConnection()
	 */
	public AmazonEC2 getConnection(String endpoint) throws DriverException,
			NotSupportedException {
		try {
			Assert.notNull(endpoint);
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}

		try {
			AmazonEC2 amazonEC2 = getClient();
			amazonEC2.setEndpoint(endpoint);
			return amazonEC2;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.amazon.Drivers#getConnection()
	 */
	public AmazonEC2 getConnection() throws DriverException,
			NotSupportedException {
		try {
			AmazonEC2 amazonEC2 = getClient();
			if (StringUtils.hasLength(amazonCredential.getEndpoint()))
				amazonEC2.setEndpoint(amazonCredential.getEndpoint());
			return amazonEC2;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.amazon.Drivers#getRegions()
	 */
	public List<AmazonRegionModel> getRegions() throws DriverException,
			NotSupportedException {
		try {
			List<AmazonRegionModel> regions = new ArrayList<AmazonRegionModel>();
			for (Region region : getConnection().describeRegions().getRegions()) {
				regions.add(new AmazonRegionModel(region));
			}
			return regions;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.amazon.Drivers#getZones()
	 */
	public List<ZoneModel> getZones() throws DriverException,
			NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}

		try {
			List<ZoneModel> zones = new ArrayList<ZoneModel>();
			DescribeAvailabilityZonesRequest request = new DescribeAvailabilityZonesRequest();
			request.setRequestCredentials(amazonCredential
					.getAmazonCredentials());
			for (AvailabilityZone zone : getConnection(
					amazonCredential.getEndpoint()).describeAvailabilityZones()
					.getAvailabilityZones()) {
				zones.add(new AmazonZoneModel(zone));
			}
			return zones;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#getImages()
	 */
	public List<AmazonImageModel> getImages() throws DriverException,
			NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}

		try {
			List<AmazonImageModel> images = new ArrayList<AmazonImageModel>();
			DescribeImagesRequest describeImagesRequest = new DescribeImagesRequest();
			describeImagesRequest.setRequestCredentials(amazonCredential
					.getAmazonCredentials());

			for (Image image : getConnection(amazonCredential.getEndpoint())
					.describeImages(describeImagesRequest).getImages()) {
				images.add(new AmazonImageModel(image));
			}
			return images;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.Drivers#getMyImages()
	 */
	@Override
	public Collection<? extends ImageModel> getMyImages()
			throws DriverException, NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}

		try {
			List<AmazonImageModel> images = new ArrayList<AmazonImageModel>();
			DescribeImagesRequest describeImagesRequest = new DescribeImagesRequest();
			describeImagesRequest.setOwners(amazonCredential.getAmazonOwners());
			describeImagesRequest.setRequestCredentials(amazonCredential
					.getAmazonCredentials());

			for (Image image : getConnection(amazonCredential.getEndpoint())
					.describeImages(describeImagesRequest).getImages()) {
				images.add(new AmazonImageModel(image));
			}
			return images;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.Drivers#getMyImages(java.lang.String)
	 */
	@Override
	public Collection<? extends ImageModel> getMyImages(String zone)
			throws DriverException, NotSupportedException {
		/* TODO PATCH!!! */
		return getMyImages();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#getSecures()
	 */
	public List<SecurityModel> getSecures() throws DriverException,
			NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}
		try {
			List<SecurityModel> securities = new ArrayList<SecurityModel>();
			DescribeSecurityGroupsRequest describeSecurityGroupsRequest = new DescribeSecurityGroupsRequest();
			describeSecurityGroupsRequest
					.setRequestCredentials(amazonCredential
							.getAmazonCredentials());
			for (SecurityGroup security : getConnection(
					amazonCredential.getEndpoint()).describeSecurityGroups(
					describeSecurityGroupsRequest).getSecurityGroups()) {
				securities.add(new AmazonSecurityModel(security));
			}
			return securities;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	public List<KeyPairModel> getKeyPairs() throws DriverException,
			NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}
		try {
			List<KeyPairModel> keypairs = new ArrayList<KeyPairModel>();
			DescribeKeyPairsRequest describeKeyPairsRequest = new DescribeKeyPairsRequest();
			describeKeyPairsRequest.setRequestCredentials(amazonCredential
					.getAmazonCredentials());

			for (KeyPairInfo key : getConnection(amazonCredential.getEndpoint())
					.describeKeyPairs(describeKeyPairsRequest).getKeyPairs()) {
				keypairs.add(new AmazonKeyPairModel(key));
			}
			return keypairs;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	private final long CHECK_PERIOD = 10 * 1000L;

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#createInstance()
	 */
	public InstanceResultModel createInstance(InstanceModel instance)
			throws DriverException, NotSupportedException {
		logger.info(">>>>>>>>>>>>>>>>>>>>>> in??? here?");
		InstanceResultModel instanceResultModel = null;
		try {
			instanceResultModel = createAmazonInstance(instance);
			logger.info("1 \n{}", instanceResultModel);
			final Collection<String> instanceIDs = instanceResultModel
					.getInstanceIds();
			logger.info("2 \n{}", instanceIDs);
			Set<AmazonInstanceModel> instances = null;
			boolean isFlag = true;
			peddingBlock: while (true) {
				Thread.sleep(CHECK_PERIOD);
				instances = getInstances(instanceIDs);
				logger.info("3 \n{}", instances);
				for (AmazonInstanceModel model : instances) {
					logger.info(">>>>>>>>> current State : {}", model.getCurrentState());
					if (logger.isDebugEnabled())
						logger.debug(
								"##### Amazon Create Instance Current State : {}",
								model.getCurrentState());

					isFlag = isFlag
							&& "running".equals(model.getCurrentState());
				}

				if (isFlag) {
					instanceResultModel.setInstances(instances);
					break peddingBlock;
				} else {
					isFlag = true;
				}
				Thread.sleep(CHECK_PERIOD);
				continue peddingBlock;
			}

			renameAmazonInstance(instanceResultModel,
					instance.getInstanceName());
			instanceResultModel.setInstances(getInstances(instanceIDs));

			return instanceResultModel;
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("##### using amazonCredential {}",
						amazonCredential);
				logger.debug("##### using instance {}", instance);
				logger.debug("##### using instanceResultModel : {}",
						instanceResultModel);
			}
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * kr.co.insoft.driver.Drivers#createInstanceWithImage(kr.co.insoft.driver
	 * .model.InstanceModel)
	 */
	@Override
	public InstanceResultModel createInstanceWithImage(InstanceModel instance)
			throws DriverException, NotSupportedException {
		InstanceResultModel instanceResultModel = null;
		try {
			instanceResultModel = createAmazonInstance(instance);
			final Collection<String> instanceIDs = instanceResultModel
					.getInstanceIds();
			Set<AmazonInstanceModel> instances = null;
			boolean isFlag = true;
			peddingBlock: while (true) {
				Thread.sleep(CHECK_PERIOD);
				instances = getInstances(instanceIDs);
				for (AmazonInstanceModel model : instances) {
					if (logger.isDebugEnabled())
						logger.debug(
								"##### Amazon Create Instance Current State : {}",
								model.getCurrentState());

					isFlag = isFlag
							&& "running".equals(model.getCurrentState());
				}

				if (isFlag) {
					instanceResultModel.setInstances(instances);
					break peddingBlock;
				} else {
					isFlag = true;
				}
				Thread.sleep(CHECK_PERIOD);
				continue peddingBlock;
			}

			renameAmazonInstance(instanceResultModel,
					instance.getInstanceName());
			instanceResultModel.setInstances(getInstances(instanceIDs));

			return instanceResultModel;
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("##### using amazonCredential {}",
						amazonCredential);
				logger.debug("##### using instance {}", instance);
				logger.debug("##### using instanceResultModel : {}",
						instanceResultModel);
			}
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#renameInstance()
	 */
	public void renameInstance(InstanceModel instance) throws DriverException,
			NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
			Assert.notNull(instance);
			Assert.notNull(instance.getInstanceName());
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}
		try {
			Tag nameTag = new Tag("Name", instance.getInstanceName());
			Collection<Tag> tags = new ArrayList<Tag>();
			tags.add(nameTag);

			Collection<String> resources = new ArrayList<String>();
			resources.add(instance.getInstanceId());

			CreateTagsRequest createTagsRequest = new CreateTagsRequest();
			createTagsRequest.setRequestCredentials(amazonCredential
					.getAmazonCredentials());
			createTagsRequest.setTags(tags);
			createTagsRequest.setResources(resources);

			getConnection(amazonCredential.getEndpoint()).createTags(
					createTagsRequest);
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#getInstance(java.lang.String[])
	 */
	@Override
	public Collection<AmazonInstanceModel> getInstances(String... instanceIds)
			throws DriverException, NotSupportedException {
		return getInstances(Arrays.asList(instanceIds));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#getInstances(java.util.Collection)
	 */
	@Override
	public Set<AmazonInstanceModel> getInstances(Collection<String> instanceIds)
			throws DriverException, NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
			Assert.notNull(instanceIds);
			Assert.notEmpty(instanceIds);
			Assert.isTrue(instanceIds.size() > 0);
		} catch (IllegalArgumentException e) {
			if (logger.isDebugEnabled()) {
				logger.debug("##### Using amazonCredential : {}",
						amazonCredential);
				logger.debug("##### Using instanceIds : {}", instanceIds);
			}
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}

		try {
			Set<AmazonInstanceModel> result = new HashSet<AmazonInstanceModel>();
			retry: for (int i = 0; i < 3; i++) {
				try {
					DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest();
					describeInstancesRequest.setInstanceIds(instanceIds);
					describeInstancesRequest
							.setRequestCredentials(amazonCredential
									.getAmazonCredentials());
					DescribeInstancesResult describeInstancesResult = getConnection(
							amazonCredential.getEndpoint()).describeInstances(
							describeInstancesRequest);
					logger.info("4 {}", describeInstancesResult);
					for (Reservation reservation : describeInstancesResult
							.getReservations()) {
						for (Instance instance : reservation.getInstances()) {
							result.add(new AmazonInstanceModel(instance));
						}
					}
				} catch (AmazonServiceException exception ) {
					if (logger.isDebugEnabled()) {
						logger.debug("########## instanceIds {}", instanceIds);
					}
					logger.error("##### AmazonServiceException", exception);
					Thread.sleep(3 * 1000L);
					if ( i >= 3 ) {
						throw exception;
					}
					continue retry;
				} catch (AmazonClientException  exception) {
					if (logger.isDebugEnabled()) {
						logger.debug("########## instanceIds {}", instanceIds);
					}
					logger.error("##### AmazonServiceException", exception);
					Thread.sleep(3 * 1000L);
					if ( i >= 3 ) {
						throw exception;
					}
					continue retry;
				}
				break retry;
			}
			
			if ( logger.isDebugEnabled() )
				logger.debug("getInstances Result : {}", result);
			return result;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	public List<AmazonInstanceModel> getInstances() throws DriverException,
			NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}

		try {
			DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest();
			describeInstancesRequest.setRequestCredentials(amazonCredential
					.getAmazonCredentials());
			DescribeInstancesResult describeInstancesResult = getConnection(
					amazonCredential.getEndpoint()).describeInstances(
					describeInstancesRequest);
			List<AmazonInstanceModel> result = new ArrayList<AmazonInstanceModel>();
			for (Reservation reservation : describeInstancesResult
					.getReservations()) {
				for (Instance instance : reservation.getInstances()) {
					result.add(new AmazonInstanceModel(instance));
				}
			}
			return result;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#startInstances(java.util.Collection)
	 */
	@Override
	public Collection<AmazonInstanceModel> startInstances(
			Collection<String> instanceIds) throws DriverException,
			NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
			Assert.notNull(instanceIds);
			Assert.notEmpty(instanceIds);
			Assert.isTrue(instanceIds.size() > 0);
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}

		try {
			Set<AmazonInstanceModel> instances = getInstances(instanceIds);

			StartInstancesRequest startInstancesRequest = new StartInstancesRequest();
			startInstancesRequest.setRequestCredentials(amazonCredential
					.getAmazonCredentials());
			startInstancesRequest.setInstanceIds(instanceIds);
			StartInstancesResult startInstancesResult = getConnection(
					amazonCredential.getEndpoint()).startInstances(
					startInstancesRequest);

			for (AmazonInstanceModel instance : instances) {
				for (InstanceStateChange state : startInstancesResult
						.getStartingInstances()) {
					if (instance.getInstanceId().equals(state.getInstanceId())) {
						instance.setCurrentState(state.getCurrentState()
								.getName());
					}
				}
			}
			return instances;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#startInstance()
	 */
	@Override
	public Collection<AmazonInstanceModel> startInstances(String... instanceIds)
			throws DriverException, NotSupportedException {
		return startInstances(Arrays.asList(instanceIds));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#stopInstances(java.lang.String[])
	 */
	@Override
	public Collection<AmazonInstanceModel> stopInstances(String... instanceIds)
			throws DriverException, NotSupportedException {
		return stopInstances(Arrays.asList(instanceIds));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#stopInstances(java.util.Collection)
	 */
	@Override
	public Collection<AmazonInstanceModel> stopInstances(
			Collection<String> instanceIds) throws DriverException,
			NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
			Assert.notNull(instanceIds);
			Assert.notEmpty(instanceIds);
			Assert.isTrue(instanceIds.size() > 0);
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}

		try {
			Set<AmazonInstanceModel> instances = getInstances(instanceIds);
			StopInstancesRequest stopInstancesRequest = new StopInstancesRequest();
			stopInstancesRequest.setRequestCredentials(amazonCredential
					.getAmazonCredentials());
			stopInstancesRequest.setInstanceIds(instanceIds);
			StopInstancesResult stopInstancesResult = getConnection(
					amazonCredential.getEndpoint()).stopInstances(
					stopInstancesRequest);

			for (AmazonInstanceModel instance : instances) {
				for (InstanceStateChange state : stopInstancesResult
						.getStoppingInstances()) {
					if (instance.getInstanceId().equals(state.getInstanceId())) {
						instance.setCurrentState(state.getCurrentState()
								.getName());
					}
				}
			}
			return instances;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * kr.co.insoft.drivers.Drivers#terminateInstances(java.util.Collection)
	 */
	@Override
	public Collection<? extends InstanceModel> terminateInstances(
			Collection<InstanceModel> instances) throws DriverException,
			NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
			Assert.notNull(instances);
			Assert.isTrue(instances.size() > 0);
			for (InstanceModel instance : instances)
				Assert.notNull(instance.getInstanceId());
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}

		try {
			Set<String> instanceIds = new HashSet<String>();
			for (InstanceModel instance : instances)
				instanceIds.add(instance.getInstanceId());

			Set<AmazonInstanceModel> deleteTargetInstances = getInstances(instanceIds);

			TerminateInstancesRequest terminateInstancesRequest = new TerminateInstancesRequest();
			terminateInstancesRequest.setInstanceIds(instanceIds);
			terminateInstancesRequest.setRequestCredentials(amazonCredential
					.getAmazonCredentials());
			TerminateInstancesResult terminateInstancesResult = getConnection(
					amazonCredential.getEndpoint()).terminateInstances(
					terminateInstancesRequest);

			for (InstanceModel deleteInstance : deleteTargetInstances) {
				for (InstanceStateChange state : terminateInstancesResult
						.getTerminatingInstances()) {
					if (deleteInstance.getInstanceId().equals(
							state.getInstanceId())) {
						deleteInstance.setCurrentState(state.getCurrentState()
								.getName());
					}
				}
			}
			return deleteTargetInstances;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#terminateInstances(java.lang.String[])
	 */
	@Override
	public Collection<? extends InstanceModel> terminateInstances(
			InstanceModel instance) throws DriverException,
			NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
			Assert.notNull(instance);
			Assert.notNull(instance.getInstanceId());
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}

		try {
			Set<String> deleteTargetInstanceIds = new HashSet<String>();
			deleteTargetInstanceIds.add(instance.getInstanceId());
			Collection<AmazonInstanceModel> deleteTargetInstances = getInstances(instance
					.getInstanceId());

			TerminateInstancesRequest terminateInstancesRequest = new TerminateInstancesRequest();
			terminateInstancesRequest.setInstanceIds(deleteTargetInstanceIds);
			terminateInstancesRequest.setRequestCredentials(amazonCredential
					.getAmazonCredentials());
			TerminateInstancesResult terminateInstancesResult = getConnection(
					amazonCredential.getEndpoint()).terminateInstances(
					terminateInstancesRequest);

			for (InstanceModel deleteInstance : deleteTargetInstances) {
				for (InstanceStateChange state : terminateInstancesResult
						.getTerminatingInstances()) {
					if (deleteInstance.getInstanceId().equals(
							state.getInstanceId())) {
						deleteInstance.setCurrentState(state.getCurrentState()
								.getName());
					}
				}
			}
			return deleteTargetInstances;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#getVolumes()
	 */
	@Override
	public List<VolumeModel> getVolumes() throws DriverException,
			NotSupportedException {
		return getVolumes(null);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#getVolumes(java.util.Collection)
	 */
	@Override
	public List<VolumeModel> getVolumes(Collection<String> volumeIds)
			throws DriverException, NotSupportedException {
		try {
			Assert.notNull(amazonCredential.getEndpoint());
		} catch (IllegalArgumentException e) {
			throw new DriverException(ExceptionCode.ARGUMENT_ERROR, e);
		}

		try {
			List<VolumeModel> result = new ArrayList<VolumeModel>();
			DescribeVolumesRequest describeVolumesRequest = new DescribeVolumesRequest();
			if (volumeIds != null)
				describeVolumesRequest.setVolumeIds(volumeIds);
			describeVolumesRequest.setRequestCredentials(amazonCredential
					.getAmazonCredentials());

			DescribeVolumesResult describeVolumesResult = getConnection(
					amazonCredential.getEndpoint()).describeVolumes(
					describeVolumesRequest);
			for (Volume volume : describeVolumesResult.getVolumes()) {
				result.add(new AmazonVolumeModel(volume));
			}
			return result;
		} catch (Exception e) {
			throw new DriverException(ExceptionCode.DRIVER_ERROR, e);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.drivers.Drivers#createVolumes()
	 */
	@Override
	public void createVolumes() throws DriverException, NotSupportedException {
		CreateVolumeRequest createVolumeRequest = new CreateVolumeRequest();
		createVolumeRequest.setRequestCredentials(amazonCredential
				.getAmazonCredentials());
		// createVolumeRequest.setAvailabilityZone(availabilityZone);
		// createVolumeRequest.setIops(iops);
		// createVolumeRequest.setSize(size);
		// createVolumeRequest.setSnapshotId(snapshotId);
		// createVolumeRequest.setVolumeType(volumeType);
		// createVolumeRequest.setVolumeType(volumeType);
		// CreateVolumeResult createVolumeResult = getConnection(
		// amazonCredential.getEndpoint()).createVolume(
		// createVolumeRequest);
	}

	public void attachVolume() throws DriverException, NotSupportedException {
		// AttachVolumeRequest attachVolumeRequest = new AttachVolumeRequest();
		// attachVolumeRequest.setRequestCredentials(amazonCredential.getAmazonCredentials());
		// attachVolumeRequest.setDevice(device);
		// attachVolumeRequest.setInstanceId(instanceId);
		// attachVolumeRequest.setVolumeId(volumeId);
		// getConnection(endpoint).attachVolume(attachVolumeRequest);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.Drivers#getHardwareProfiles()
	 */
	@Override
	public List<? extends HardwareProfileModel> getHardwareProfiles()
			throws DriverException, NotSupportedException {
		List<AmazonHardwareProfileModel> types = new ArrayList<AmazonHardwareProfileModel>();
		for (InstanceType type : Arrays.asList(InstanceType.values())) {
			types.add(new AmazonHardwareProfileModel(type));
		}
		return types;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.Drivers#getHardwareProfiles(java.lang.String)
	 */
	@Override
	public Collection<? extends HardwareProfileModel> getHardwareProfiles(
			String zone) throws DriverException, NotSupportedException {
		return getHardwareProfiles();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.Drivers#getOsTypes()
	 */
	@Override
	public List<AmazonOsTypeModel> getOsTypes() throws DriverException,
			NotSupportedException {
		throw new NotSupportedException(ExceptionCode.NOT_SUPPORTED);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.Drivers#getCurrentState()
	 */
	@Override
	@Async
	public Collection<? extends InstanceModel> getCurrentState()
			throws DriverException, NotSupportedException {
		return getInstances();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see kr.co.insoft.driver.Drivers#getCurrentState(java.util.Collection)
	 */
	@Override
	@Async
	public Collection<? extends InstanceModel> getCurrentState(
			Collection<? extends InstanceModel> instances)
			throws DriverException, NotSupportedException {
		Set<String> instanceIds = new HashSet<String>();
		for (InstanceModel model : instances)
			instanceIds.add(model.getInstanceId());
		return getInstances(instanceIds);
	}
}
